package project.tool4nat.client.handler;

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import project.tool4nat.common.ChannelManager;
import project.tool4nat.common.Config;
import project.tool4nat.common.Constants;
import project.tool4nat.common.DataPack;

public class ClientTunnelHandler extends SimpleChannelInboundHandler<DataPack> {

    private final static Logger logger = LoggerFactory.getLogger(ClientTunnelHandler.class);

    private EventLoopGroup group;

    private Bootstrap bootstrap;

    public ClientTunnelHandler(EventLoopGroup group) {
        this.group = group;
        bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new ContentServerHandler());
                    }
                });
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        logger.info("client tunnel active");
        ChannelManager.setProxyTunnel(ctx.channel());
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        logger.info("client tunnel inactive");
        ChannelManager.setProxyTunnel(null);
        super.channelInactive(ctx);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, DataPack msg) throws Exception {
        long cid = msg.getCid();
        logger.info("receive msg from proxy server, cid={}", cid);
        switch (msg.getType()) {
            case Constants.TYPE_DISCONNECT:
                doDisconnect(ctx.channel());
                break;
            case Constants.TYPE_TRANSFER:
                doTransfer(msg);
                break;
        }
        doTransfer(msg);
    }

    private void doDisconnect(Channel channel) {
        logger.info("client idle, disconnect");
        ChannelManager.setProxyTunnel(null);
        channel.close();
    }

    private void doTransfer(DataPack msg) {
        long cid = msg.getCid();
        Channel contentChannel = ChannelManager.getCaller(cid);
        if (contentChannel == null) {
            try {
                bootstrap.connect(Config.CONTENT_SERVER_HOST, Config.CONTENT_SERVER_PORT)
                        .addListener(new ChannelFutureListener() {
                            @Override
                            public void operationComplete(ChannelFuture future) throws Exception {
                                if (future.isSuccess()) {
                                    ChannelManager.addCaller(cid, future.channel());
                                    byte[] data = msg.getData();
                                    future.channel().attr(Constants.CID_KEY).set(msg.getCid());
                                    future.channel().writeAndFlush(Unpooled.wrappedBuffer(data));
                                }
                            }
                        }).sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            contentChannel.writeAndFlush(Unpooled.wrappedBuffer(msg.getData()));
        }

    }
}
